#ifndef	RENDER_RESOURCE_H
#define	RENDER_RESOURCE_H

#include "stdafx.h"
#if defined(WIN32) && !defined(_CONSOLE)
#include <GL/glew.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include "ase.h"

typedef unsigned long DWORD;

class DecoColor
{
public:
	BYTE R,	G,B,A;
public:

	DWORD& DWColor(void) {return *((DWORD*)this);}
	const DWORD& DWColor(void) const {return *((DWORD*)this);}

	// Constructors.
	DecoColor()	{}
	DecoColor( BYTE	InR, BYTE InG, BYTE	InB, BYTE InA =	255	)
		:	R(InR),	G(InG),	B(InB),	A(InA) {}
		DecoColor( const vector4& P	)
			:	R(Clamp<BYTE>(static_cast<BYTE>(P.x*255),0,255))
			,	G(Clamp<BYTE>(static_cast<BYTE>(P.y*255),0,255))
			,	B(Clamp<BYTE>(static_cast<BYTE>(P.z*255),0,255))
			,	A(Clamp<BYTE>(static_cast<BYTE>(P.w*255),0,255))
		{}
		DecoColor( DWORD InColor )
		{ DWColor()	= InColor; }

		// Operators.
		BOOL operator==( const DecoColor &C	) const
		{
			return DWColor() ==	C.DWColor();
		}
		BOOL operator!=( const DecoColor& C	) const
		{
			return DWColor() !=	C.DWColor();
		}
		void operator+=(DecoColor C)
		{

			R =	(BYTE) min((INT) R + (INT) C.R,255);
			G =	(BYTE) min((INT) G + (INT) C.G,255);
			B =	(BYTE) min((INT) B + (INT) C.B,255);
			A =	(BYTE) min((INT) A + (INT) C.A,255);
		}
		INT	Brightness() const
		{
			return (2*(INT)R + 3*(INT)G	+ 1*(INT)B)>>3;
		}
		FLOAT FBrightness()	const
		{
			return (2.f*R +	3.f*G +	1.f*B)/(6.f*256.f);
		}
		DWORD TrueColor() const
		{
			return DWColor();
		}
		WORD HiColor565() const
		{
			return ((R >> 3) <<	11)	+ ((G >> 2)	<< 5) +	(B >> 3);
		}
		WORD HiColor555() const
		{
			return ((R >> 3) <<	10)	+ ((G >> 3)	<< 5) +	(B >> 3);
		}
		WORD HiColor4444() const
		{
			return ((A >> 4) <<	12)	+ ((R >> 4)	<< 8) +	((G	>> 4) << 4)	+ (B >>	4);
		}
		vector4 ToVec4() const
		{
			return vector4(R/255.f,	G/255.f, B/255.f, A/255.f);
		}
		vector3 ToVec3() const
		{
			return vector3(R/255.f,	G/255.f, B/255.f);
		}
		operator vector4() const
		{
			return vector4(R/255.f,	G/255.f, B/255.f, A/255.f);
		}
		operator vector3() const
		{
			return vector3(R/255.f,	G/255.f, B/255.f);
		}
		operator DWORD() const 
		{ 
			return DWColor(); 
		}
		friend DecoArchive& operator<< (DecoArchive& Ar, const DecoColor& c);
		friend DecoArchive& operator>> (DecoArchive& Ar, DecoColor& c);
		friend DecoColor operator* (DOUBLE ratio, const DecoColor& col)
		{
			DecoColor ret;
			ret.R = (Clamp<BYTE>(static_cast<BYTE>(col.R*ratio),0,255));
			ret.G = (Clamp<BYTE>(static_cast<BYTE>(col.G*ratio),0,255));
			ret.B = (Clamp<BYTE>(static_cast<BYTE>(col.B*ratio),0,255));
			//ret.A = (Clamp<BYTE>(static_cast<BYTE>(col.A*ratio),0,255));	
			return ret;
		}
		friend DecoColor operator* (const DecoColor& col, DOUBLE ratio)
		{
			return ratio * col;
		}
};


const BYTE USE_COLOR_STREAM = 1;
const BYTE USE_NORMAL_STREAM = 1 << 1;
const BYTE USE_TEXCOORD_STREAM = 1 << 2;

class DecoMaterial;

class DecoVertexBuffer
{
private:
	vector3*			m_vertices;
	DecoColor*			m_colors;
	vector3*			m_normals;
	vector3*			m_texcoords;   // z is rarely used , but exists in ASE files
	size_t				m_nVertices;
	INT					m_iMaterialRef; // m_iMaterialRef reference to a material, 
	// or it equals to -1 if reference nothing
	// size_t numNormals;
	// size_t numTexcoords; // these three variables should be the same
	void CopyFrom(const DecoVertexBuffer& rhs);
	void ClearEverything();
public:
	BYTE fieldFlag;

	DecoVertexBuffer() : m_vertices(NULL), m_colors(NULL), m_normals(NULL),
		m_texcoords(NULL), m_nVertices(0), fieldFlag(0), m_iMaterialRef(-1)
	{}

	DecoVertexBuffer(const DecoVertexBuffer& rhs) : m_vertices(NULL), m_colors(NULL), m_normals(NULL),
		m_texcoords(NULL), m_nVertices(0), fieldFlag(0), m_iMaterialRef(-1)
	{
		CopyFrom(rhs);
	}

	DecoVertexBuffer& operator=(const DecoVertexBuffer& rhs)
	{
		if (this == &rhs) return *this;
		ClearEverything();
		CopyFrom(rhs);
		return *this;
	}

	// SetVertexInformation must be called before the latter 3 methods
	void SetVertexInformation(size_t NumVertices,
		vector3* InVertices, 		
		vector3* InNormals = NULL,
		vector3* InTexcoords = NULL,
		DecoColor* InColors = NULL,
		BOOL bCopy = TRUE);

	// void FillColorInformation(const DecoColor* const InColors);
	// void FillNormalInformation(const vector3* const InNormals, size_t NumNormals);
	// void FillTexcoordInformation(const vector3* const InTexcoords, size_t numTexcoords);

	// initialize static mesh section from a ase_file's mesh section's content
	BOOL Init(const ASE::GeomObjectMesh& mesh, Box& outBoundingBox);
	
	// initialize static mesh section from a saved binary file;
	BOOL ReadFromFile(ifstream& infile, Box& outBoundingBox);
	
	// save to a binary file;
	VOID WriteToFile(ofstream& outFile);

	// Exchange Y and Z axis
	VOID ExchangeYZAxis();

	// Move the model to center
	VOID CenterModel(vector3 center);

	VOID SetColorBufferForTransparent(DecoMaterial* pMaterial);

	vector3* GetVertices()
	{ return m_vertices; }

	DecoColor* GetColors()
	{ return m_colors; }

	vector3* GetNormals()
	{ return m_normals; }

	vector3* GetTexcoords()
	{ return m_texcoords; }


	vector3& Vertices(size_t Index)
	{
		assert(Index >= 0 && Index < m_nVertices);
		return m_vertices[Index];
	}

	const vector3& Vertices(size_t Index) const
	{
		assert(Index >= 0 && Index < m_nVertices);
		return m_vertices[Index];
	}

	vector3 Texcoords(size_t Index)
	{
		assert(Index >= 0 && Index < m_nVertices);
		return m_texcoords[Index];
	}

	DecoColor Colors(size_t Index)
	{
		assert(Index >= 0 && Index < m_nVertices);
		return m_colors[Index];
	}

	vector3 Normals(size_t Index)
	{
		assert(Index >= 0 && Index < m_nVertices);
		return m_normals[Index];
	}

	size_t NumberVertices() const 
	{
		return m_nVertices;
	}

	INT GetMaterialRef() const 
	{
		return m_iMaterialRef;
	}


	VOID SetMaterialRef(INT materialRef)
	{
		m_iMaterialRef = materialRef;
	}

	BOOL ParseFieldFlag(const BYTE Field) const
	{
		return ((Field & fieldFlag) != 0);
	}

	~DecoVertexBuffer();
};

class DecoIndexBuffer
{
private:
	SHORT* indices;
	size_t numIndices;

	void ClearEverything();
	void CopyFrom(const DecoIndexBuffer& rhs);
public:
	DecoIndexBuffer() : indices(NULL), numIndices(0)
	{}
	DecoIndexBuffer(const DecoIndexBuffer& rhs) : indices(NULL), numIndices(0)
	{
		CopyFrom(rhs);
	}
	DecoIndexBuffer& operator= (const DecoIndexBuffer& rhs)
	{
		if (&rhs == this) return *this;
		ClearEverything();
		CopyFrom(rhs);
		return *this;
	}
	SHORT& operator[] (size_t Index)
	{
		assert(Index >= 0 && Index < numIndices);
		return indices[Index];
	}
	size_t NumIndices()
	{
		return numIndices;
	}
	void SetIndexInformation(const SHORT* const InIndices, size_t NumElement);
	void SetIndexInformation(const INT* const InIndices, size_t NumElement);
	~DecoIndexBuffer()
	{
		ClearEverything();
	}
};



class DecoPixels
{
public:
	// constructor and copy constructor
	DecoPixels();
	DecoPixels(const DecoPixels& rhs);
	virtual ~DecoPixels();

	// assignment operator
	DecoPixels& operator= (const DecoPixels& rhs);

	// this function should only be used when parsing ase file (because its file name is absolute.)
	virtual BOOL LoadImage(const std::string& path, const std::string& filename);
	// this function should be used when loading a general image
	virtual BOOL LoadImage(const std::string& fullpath);
	virtual VOID Clear();

	BOOL		 valid() const { return m_valid; }

	VOID		 SetFormat(GLenum format);
	GLenum		 GetFormat() const { return m_format;	}

	VOID		 SetType(GLenum type);
	GLenum		 GetType() const {return m_type;}
	UINT		 GetNumBytes() const {return m_num_bytes;}

	const BYTE*  GetPixels() const { return m_pixels; }

protected:
	INT			size() const;
	BYTE*		m_pixels;
	UINT		m_width;
	UINT		m_height;
	GLenum		m_format;		// the format of pixel data, such as GL_RGB, GL_RGBA ...
	UINT		m_num_bytes;	// the number of bytes used in one pixel. (GL_RGB: 3, GL_RGBA: 4)
	GLenum		m_type;			// the data type of pixel data, such as GL_UNSIGNED_BYTE, GL_BYTE ...
	BOOL		m_valid;

	BOOL		m_bJPEG;
	size_t		m_JPEGFileLength;
	BYTE*		m_pJPEGFile;

	VOID	CopyFrom(const DecoPixels& rhs);
	BYTE*	LoadTGA(UINT *width, UINT *height, UINT *bits, const char *filename);
	//BYTE*	LoadJPEG(UINT *width, UINT *height, UINT *bits, const char *filename);

	BOOL	ReadFromFile(ifstream& inFile, bool bJPEG);
	VOID	WriteToFile(ofstream& outFile, bool bJPEG);
};

// DecoTexure here corresponds to MAP_DIFFUSE sections in ASE
// or MAP_AMBIENT, MAP_OPACITY, MAP_SPECULAR ...
class DecoTexture : public DecoPixels
{
public:
	enum DecoTextureType{DecoTextureDiffuse, DecoTextureAmbient, DecoTextureOpacity, 
		DecoTextureSpecular, DecoTextureBump, DecoTextureReflect};

	DecoTexture();
	DecoTexture(const DecoTexture& rhs);
	virtual ~DecoTexture();

	DecoTexture& operator= (const DecoTexture& rhs);

	// virtual BOOL	LoadImage(const std::string filename);
	virtual VOID	Clear();
	VOID			Apply() const ;
	BOOL			Init(const ASE::Map& texture, DecoTextureType);
	BOOL			Init(const string& filename, DecoTextureType);
	BOOL			ReadFromFile(ifstream& inFile, bool bJPEG);
	VOID			WriteToFile(ofstream& outFile, bool bJPEG);

	VOID	SetUnit(GLenum unit) { m_unit = unit; }
	GLenum	GetUnit() const { return m_unit;	}

	VOID	SetMode(GLint mode) { m_mode = mode; }
	GLint	GetMode() const { return m_mode; }

	GLuint	GetTexID() const	{ return m_texID; }

	VOID	SetUOffset(FLOAT f) { m_u_offset = f; }
	FLOAT	GetUOffset() const	{ return m_u_offset; }

	VOID	SetVOffset(FLOAT f) { m_v_offset = f; }
	FLOAT	GetVOffset() const	{ return m_v_offset; }

	VOID	SetUTiling(FLOAT f) { m_u_tiling = f; }
	FLOAT	GetUTiling() const	{ return m_u_tiling; }

	VOID	SetVTiling(FLOAT f) { m_v_tiling = f; }
	FLOAT	GetVTiling() const	{ return m_v_tiling; }

	VOID	SetUVWAngle(FLOAT f){m_uvw_angle = f; }
	FLOAT	GetUVWAngle() const { return m_uvw_angle; }


protected:
	virtual BOOL	InitTextureObject();
	VOID			CopyFrom(const DecoTexture& rhs);
	GLuint			m_texID;
	BOOL			m_textureObjectAllocated;
	GLenum			m_unit;
	GLint			m_mode;
	DecoTextureType m_textureType;

	// we use these attributes to manipulate texture coordinates. 
	FLOAT			m_u_offset;
	FLOAT			m_v_offset;
	FLOAT			m_u_tiling;
	FLOAT			m_v_tiling;
	FLOAT			m_uvw_angle;

};

class DecoMaterial
{
	friend class DecoStaticMeshSection;
	friend class DecoVertexBuffer;
public:
	DecoMaterial() : m_valid(FALSE), m_flags(0), m_num_of_sub_material(0), m_transparency(1.0f), m_shininess(0.0f),
					m_shinestrength(0.0f)
	{
		m_ambient[0] = m_ambient[1] = m_ambient[2] = 0.0f;
		m_ambient[3] = 1.0f;
		m_diffuse[0] = m_diffuse[1] = m_diffuse[2] = 0.0f;
		m_diffuse[3] = 1.0f;
		m_specular[0] = m_specular[1] = m_specular[2] = 0.0f;
		m_specular[3] = 1.0f;		
	}

	DecoMaterial(const DecoMaterial&);
	DecoMaterial& operator=(const DecoMaterial&);
	~DecoMaterial();

	VOID			Clear();
	VOID			CopyFrom(const DecoMaterial&);	

	// Initialize DecoMaterial From a material in ASE file
	BOOL			Init(const ASE::Material& material);
	BOOL			IsValid() const {return m_valid;}
	BOOL			IsTransparent() const {return signedAbs(m_transparency - 1.f) > EPSILON_FLOAT;}

	BOOL			ReadFromFile(ifstream& inFile);
	VOID			WriteToFile(ofstream& outFile);

	VOID			Apply() const;

	VOID			SetAmbient	(const vector4& v){m_ambient[0] = (FLOAT)v.x; m_ambient[1] = (FLOAT)v.y; m_ambient[2] = (FLOAT)v.z; m_ambient[3] = (FLOAT)v.w;}
	VOID			SetDiffuse	(const vector4& v){m_diffuse[0] = (FLOAT)v.x; m_diffuse[1] = (FLOAT)v.y; m_diffuse[2] = (FLOAT)v.z; m_diffuse[3] = (FLOAT)v.w;}
	VOID			SetSpecular (const vector4& v){m_specular[0] = (FLOAT)v.x; m_specular[1] = (FLOAT)v.y; m_specular[2] = (FLOAT)v.z; m_specular[3] = (FLOAT)v.w;}
	VOID			SetTransparency (FLOAT transparency);
	// load a image file as texture for diffuse map
	VOID			SetDiffuseTexture(const string& filename);
	vector3			GetAmbient() const;
	vector3			GetSpecular() const;
	vector3			GetDiffuse() const;
	// Get An Usable Diffuse Texture and return it's pointer
	// Otherwise return NULL.
	const DecoTexture*    GetFirstMapDiffuse() const;

private:

	FLOAT						m_ambient[4];
	FLOAT						m_diffuse[4];
	FLOAT						m_specular[4];
	BOOL						m_valid;
			
	FLOAT						m_transparency; // 0 -> totally not transparent
												// 1 -> totally transparent

	FLOAT						m_shininess;
	FLOAT						m_shinestrength;

	UINT						m_flags;   // indicates Material's attributes;

	DecoTexture					m_texture_diffuse;
	DecoTexture					m_texture_ambient;
	DecoTexture					m_texture_opacity;
	DecoTexture					m_texture_specular;
	DecoTexture     			m_texture_bump;
	DecoTexture					m_texture_reflect;

	INT							m_num_of_sub_material;
	std::vector<DecoMaterial*>	m_sub_materials;
};



#endif
